package com.zbkj.service.service.device.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zbkj.common.config.AliMqttConfig;
import com.zbkj.common.exception.AppException;
import com.zbkj.common.model.device.Device;
import com.zbkj.common.model.device.MqttConfig;
import com.zbkj.common.request.MonitorControlLightRequest;
import com.zbkj.common.request.device.DeviceRequest;
import com.zbkj.common.utils.mqtt.ConnectionOptionWrapper;
import com.zbkj.service.dao.device.MqttConfigDao;
import com.zbkj.service.service.device.DeviceService;
import com.zbkj.service.service.device.MqttConfigService;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

/**
 * MQTT配置服务实现类
 * +----------------------------------------------------------------------
 * | Author: xiaolic
 * +----------------------------------------------------------------------
 */
@Slf4j
@Service
public class MqttConfigServiceImpl extends ServiceImpl<MqttConfigDao, MqttConfig> implements MqttConfigService {

    @Value("${aliyun.mqtt.accessKeyId}")
    private String accessKeyId;

    @Value("${aliyun.mqtt.accessKeySecret}")
    private String accessKeySecret;

    @Value("${aliyun.mqtt.instanceId}")
    private String instanceId;

    @Value("${aliyun.mqtt.regionId}")
    private String regionId;

    @Value("${aliyun.mqtt.serverUrl}")
    private String serverUrl;

    @Autowired
    private DeviceService deviceService;

    @Autowired
    private AliMqttConfig aliMqttConfig;

    /**
     * 获取默认的MQTT配置
     *
     * @return MQTT配置信息
     */
    @Override
    public MqttConfig getDefaultConfig() {
        // 获取第一条可用的MQTT配置作为默认配置
        LambdaQueryWrapper<MqttConfig> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MqttConfig::getIsDeleted, false);
        queryWrapper.last("LIMIT 1");

        MqttConfig config = getOne(queryWrapper);
        if (config == null) {
            log.error("系统中没有可用的MQTT配置");
            throw new AppException("系统中没有可用的MQTT配置");
        }

        return config;
    }

    /**
     * 根据设备ID获取MQTT配置
     *
     * @param deviceId 设备ID
     * @return MQTT配置信息
     */
    @Override
    public MqttConfig getConfigByDeviceId(Long deviceId) {
        if (deviceId == null || deviceId <= 0) {
            return getDefaultConfig();
        }

        // 查找设备信息
        Device device = deviceService.getDeviceById(deviceId);
        if (device == null) {
            log.warn("设备不存在，设备ID: {}", deviceId);
            return getDefaultConfig();
        }

        // 查找设备关联的MQTT配置
        if (device.getMqttConfigId() != null && device.getMqttConfigId() > 0) {
            MqttConfig config = getById(device.getMqttConfigId());
            if (config != null && !config.getIsDeleted()) {
                return config;
            }
        }

        // 如果设备没有关联配置或配置不可用，返回默认配置
        return getDefaultConfig();
    }

    @Override
    public MqttConfig getConfigByDevice(DeviceRequest request) throws Exception {
        MqttConfig mqttConfig = null;
        if (StrUtil.isBlank(request.getDeviceSN())
            && StrUtil.isBlank(request.getMacAddress())
            && StrUtil.isBlank(request.getImei())) {
            return mqttConfig;
        }

        Device device = null;

        if (StrUtil.isNotBlank(request.getDeviceSN())) {
            device = deviceService.getDeviceBySn(request.getDeviceSN());
        } else if (StrUtil.isNotBlank(request.getMacAddress())) {
            device = deviceService.getDeviceByMac(request.getMacAddress());
        } else if (StrUtil.isNotBlank(request.getImei())) {
            device = deviceService.getDeviceByImei(request.getImei());
        }

        if (ObjectUtil.isNull(device)) {
            return mqttConfig;
        }

        Long configId = device.getMqttConfigId();

        if (Objects.equals(configId, null)) {
            mqttConfig = registerDeviceToAliMqtt(device);
        } else {
            mqttConfig = getById(configId);
            if (ObjectUtil.isNull(mqttConfig)) {
                mqttConfig = registerDeviceToAliMqtt(device);
            }
        }

        mqttConfig.setDeviceSN(device.getSn());

        // 检查并生成直连用户名和密码
        boolean needUpdate = false;
        if (StrUtil.isBlank(mqttConfig.getConnUsername()) || StrUtil.isBlank(mqttConfig.getConnPassword())) {
            // 确保有accessKey和secretKey
            if (StrUtil.isBlank(mqttConfig.getAccessKey()) || StrUtil.isBlank(mqttConfig.getSecretKey())) {
                // 如果没有accessKey或secretKey，需要重新注册获取
                log.info("设备MQTT配置缺少accessKey或secretKey，重新注册获取，设备SN: {}", device.getSn());
                mqttConfig = registerDeviceToAliMqtt(device);
            }

            // 生成MQTT直连凭证
            generateMqttCredentials(mqttConfig);
            needUpdate = true;
        }

        // 如果有更新，保存到数据库
        if (needUpdate) {
            mqttConfig.setUpdatedAt(new Date());
            updateById(mqttConfig);
            log.info("已更新设备MQTT直连凭证，设备SN: {}", device.getSn());
        }

        return mqttConfig;
    }

    /**
     * 调用阿里云API注册设备并获取访问凭证
     *
     * @param device 设备信息
     * @return MQTT配置信息
     */
    private MqttConfig registerDeviceToAliMqtt(Device device) throws Exception {
        if (StrUtil.isBlank(accessKeyId) || StrUtil.isBlank(accessKeySecret) ||
                StrUtil.isBlank(instanceId) || StrUtil.isBlank(regionId)) {
            throw new AppException("阿里云MQTT配置信息不完整");
        }

        // 设备唯一标识，使用设备SN或MAC地址
        String clientId = "GID_bm47@@@" + device.getSn();

        // 创建阿里云API客户端
        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
                .setAccessKeyId(accessKeyId)
                .setAccessKeySecret(accessKeySecret)
                .setRegionId(regionId);
        config.endpoint = "onsmqtt." + regionId + ".aliyuncs.com";

        com.aliyun.onsmqtt20200420.Client client = new com.aliyun.onsmqtt20200420.Client(config);

        // 创建注册设备凭证请求
        com.aliyun.onsmqtt20200420.models.RegisterDeviceCredentialRequest request =
                new com.aliyun.onsmqtt20200420.models.RegisterDeviceCredentialRequest()
                        .setClientId(clientId)
                        .setInstanceId(instanceId);

        // 发送请求并获取响应
        com.aliyun.onsmqtt20200420.models.RegisterDeviceCredentialResponse response =
                client.registerDeviceCredential(request);

        // 创建MQTT配置对象
        MqttConfig mqttConfig = new MqttConfig();
        mqttConfig.setClientId(response.body.deviceCredential.clientId);
        mqttConfig.setInstanceId(response.body.deviceCredential.instanceId);
        mqttConfig.setAccessKey(response.body.deviceCredential.deviceAccessKeyId);
        mqttConfig.setSecretKey(response.body.deviceCredential.deviceAccessKeySecret);
        mqttConfig.setMqttServerUrl(serverUrl);
        mqttConfig.setCreatedAt(new Date());
        mqttConfig.setUpdatedAt(new Date());
        mqttConfig.setIsDeleted(false);
        save(mqttConfig);

        device.setMqttConfigId(mqttConfig.getId());
        device.setUpdatedAt(new Date());
        deviceService.updateById(device);

        return mqttConfig;
    }

    /**
     * 将MQTT配置信息格式化为设备所需的返回格式
     *
     * @param config MQTT配置信息
     * @return 格式化后的字符串
     */
    @Override
    public String formatMqttConfigResponse(MqttConfig config) {
        if (config == null) {
            log.error("MQTT配置为空");
            throw new AppException("MQTT配置为空");
        }

        // 生成随机字符串，用于混淆加密内容
        String randomStr = generateRandomString(10);

        // 格式化为逗号分隔的字符串
        StringBuilder sb = new StringBuilder();
        sb.append(randomStr).append(",");
        sb.append(config.getInstanceId()).append(",");
        sb.append(config.getGroupId()).append(",");
        sb.append(config.getTopicId()).append(",");
        sb.append(config.getMqttServerUrl()).append(",");
        sb.append(config.getAccessKey()).append(",");
        sb.append(config.getSecretKey());

        return sb.toString();
    }

    /**
     * 生成指定长度的随机字符串
     *
     * @param length 长度
     * @return 随机字符串
     */
    private String generateRandomString(int length) {
        String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        StringBuilder sb = new StringBuilder(length);
        Random random = new Random();

        for (int i = 0; i < length; i++) {
            int index = random.nextInt(characters.length());
            sb.append(characters.charAt(index));
        }

        return sb.toString();
    }

    /**
     * 为MQTT配置生成直连凭证（用户名和密码）
     * 用户名格式：DeviceCredential|accessKey|instanceId
     * 密码算法：使用secretKey对clientId进行HMAC-SHA1签名，再Base64编码
     *
     * @param mqttConfig MQTT配置信息
     * @throws Exception 加密过程中可能出现的异常
     */
    private void generateMqttCredentials(MqttConfig mqttConfig) throws Exception {
        if (StrUtil.isBlank(mqttConfig.getAccessKey()) ||
            StrUtil.isBlank(mqttConfig.getSecretKey()) ||
            StrUtil.isBlank(mqttConfig.getClientId())) {
            throw new AppException("生成MQTT直连凭证失败：缺少必要参数");
        }

        // 生成Username: DeviceCredential|accessKey|instanceId
        String username = String.format("DeviceCredential|%s|%s",
                mqttConfig.getAccessKey(), instanceId);

        // 生成Password: 使用secretKey对clientId进行HMAC-SHA1签名，再Base64编码
        String password = hmacSha1(mqttConfig.getClientId(), mqttConfig.getSecretKey());

        log.info("生成MQTT直连凭证，clientId: {}", mqttConfig.getClientId());

        // 设置到MqttConfig对象
        mqttConfig.setConnUsername(username);
        mqttConfig.setConnPassword(password);
    }

    /**
     * 使用HMAC-SHA1算法对数据进行签名
     *
     * @param data 待签名数据
     * @param key 密钥
     * @return Base64编码的签名结果
     * @throws Exception 加密过程中可能出现的异常
     */
    private String hmacSha1(String data, String key) throws Exception {
        SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA1");
        Mac mac = Mac.getInstance("HmacSHA1");
        mac.init(signingKey);
        byte[] rawHmac = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(rawHmac);
    }


    @Override
    public void publish(String clientId, String topic, String msg) throws MqttException, NoSuchAlgorithmException, InvalidKeyException {
        MemoryPersistence memoryPersistence = new MemoryPersistence();
        MqttClient mqttClient = new MqttClient("tcp://" + aliMqttConfig.serverUrl, clientId, memoryPersistence);
        mqttClient.setTimeToWait(5000);
        ConnectionOptionWrapper connectionOptionWrapper = new ConnectionOptionWrapper(aliMqttConfig.instanceId,
                aliMqttConfig.accessKeyId, aliMqttConfig.accessKeySecret, clientId);

        mqttClient.connect(connectionOptionWrapper.getMqttConnectOptions());
        final int qosLevel = 0;
        MqttMessage message = new MqttMessage(msg.getBytes());
        message.setQos(qosLevel);
        mqttClient.publish(topic, message);
        // 断开连接
        try {
            mqttClient.disconnect();
        } catch (MqttException e) {
            log.warn("MQTT客户端关闭连接失败");
        }
    }

    /**
     * 推送MQTT消息
     * @param requestVo
     * @throws Exception
     */
    @Override
    public void publishControlCmd(MonitorControlLightRequest requestVo) throws Exception {
        String mq4IotTopic = aliMqttConfig.parentTopic + "/bf29/" + "serverdata";
        String mac = requestVo.getDeviceMac();
        if (mac.contains(":")) {
            mac = mac.replace(":", "");
        }
        log.info("亮灯，推送消息：{}", mac);
        this.publish(aliMqttConfig.clientId1, mq4IotTopic, mac);
    }

}
